package cn.fz.utils;

import cn.hutool.core.io.resource.ClassPathResource;
import lombok.extern.slf4j.Slf4j;

import javax.net.ssl.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyStore;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Map;

/**
 * 请求海上船舶接口证书工具类
 */
@Slf4j
public class SSLServiceUtils {
    //**底下main**方法示例运行该工具类*
    //请将clientPasswordP12、clientPasswordJks、caPasswordJks替换为提供的密码
    //**只需修改以下参数*
    private static final String serverUrl = "https://ycdg.fjof.com:181/dataCenter/api/v1.0.0/systemTime";//**访问路径*
    // 下面这个地址要怎么获取到resources下面的certificate里面的client.jks，这个要怎么写
    private static final String clientJks = "certificate/client.jks";//jks**格式* *客户端证书路径*
    private static final String clientPasswordP12 = "!YpagpsWvlMB2y84";//p12**格式时设置的客户端证书密码*
    private static final String clientPasswordJks = "!YpagpsWvlMB2y84";//jks**格式时设置的客户端证书密码*
    private static final String caJks = "certificate/ca.jks"; //jks**格式 ca**根证书路径*
    private static final String caPasswordJks = "!YpagpsWvlMB2y84";//jks**格式时设置的ca**根证书密码*
    private SSLSocketFactory sslFactory = null;

    public String get(String url, String body, Map<String, String> header) {
        return doRequest("GET", url, body, header);
    }

    private String doRequest(String method, String url, String body, Map<String, String> header){
        int responseCode;
        String responseBody;
        try {
            HttpURLConnection connection = doHttpRequest(url, method, body, header);
            responseCode = getResponseCode(connection);
            responseBody = getResponseBodyAsString(connection);
            if (responseCode != 200){
                String errMsg = "请求失败，状态码："+responseCode+",错误消息：" + responseBody;
                throw new RuntimeException(errMsg);
            }
            connection.disconnect();
            return responseBody;
        } catch (Exception e) {
            printRequestErrorLog(url,body,header,e);
            throw new RuntimeException(e);
        }
    }

    private void printRequestErrorLog(String url, String body, Map<String, String> header, Exception e){
        log.error("请求url:{},请求body:{},请求header:{},错误消息:{}",url,body,header,e.getMessage());
    }

    public String post(String url, String body, Map<String, String> header) {
        return doRequest("POST", url, body, header);
    }

    private synchronized SSLSocketFactory getSSLFactory() throws Exception {
        if (sslFactory == null) {
            SSLContext sslContext = SSLContext.getInstance("SSL");
            TrustManager[] tm = {new MyX509TrustManager()};
            KeyStore truststore = KeyStore.getInstance("JKS");

            ClassPathResource resource = new ClassPathResource(clientJks);
            //**这里加载的是客户端证书*
            truststore.load(resource.getStream(), clientPasswordJks.toCharArray());
            //**上边的密码是生成jks**时的密码*
            KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
            //**这个密码是生成p12**时的密码*
            kmf.init(truststore, clientPasswordP12.toCharArray());
            sslContext.init(kmf.getKeyManagers(), tm, new java.security.SecureRandom());
            sslFactory = sslContext.getSocketFactory();
        }
        return sslFactory;
    }

    private HttpURLConnection doHttpRequest(String requestUrl, String method, String body, Map<String, String> header) throws Exception {
        HttpURLConnection conn;
        if (method == null || method.length() == 0) {
            method = "GET";
        }
        if ("GET".equals(method) && body != null && !body.isEmpty()) {
            requestUrl = requestUrl + "?" + body;
        }
        URL url = new URL(requestUrl);
        // start**这一段代码必须加在open**之前，即支持ip**访问的关键代码*
        HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
            public boolean verify(String hostname, SSLSession sslsession) {
                return true;
            }
        });
        //end*
        conn = (HttpURLConnection) url.openConnection();
        conn.setDoOutput(true);
        conn.setDoInput(true);
        conn.setUseCaches(false);
        conn.setInstanceFollowRedirects(true);
        conn.setRequestMethod(method);
        if (requestUrl.matches("^(https?)://.*$")) {
            ((HttpsURLConnection) conn).setSSLSocketFactory(this.getSSLFactory());
        }
        if (header != null) {
            for (String key : header.keySet()) {
                conn.setRequestProperty(key, header.get(key));
            }
        }
        if (body != null && !body.isEmpty()) {
            if (!method.equals("GET")) {
                OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
                wr.write(body);
                wr.close();
            }
        }
        conn.connect();
        return conn;
    }

    public int getResponseCode(HttpURLConnection connection) throws IOException {
        return connection.getResponseCode();
    }

    public String getResponseBodyAsString(HttpURLConnection connection) throws Exception {
        BufferedReader reader = null;
        if (connection.getResponseCode() == 200) {
            reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
        } else {
            reader = new BufferedReader(new InputStreamReader(connection.getErrorStream()));
        }
        StringBuffer buffer = new StringBuffer();
        String line = null;
        while ((line = reader.readLine()) != null) {
            buffer.append(line);
        }
        return buffer.toString();
    }

    class MyX509TrustManager implements X509TrustManager {
        private X509TrustManager sunJSSEX509TrustManager;

        MyX509TrustManager() throws Exception {
            // create a "default" JSSE X509TrustManager.*
            KeyStore ks = KeyStore.getInstance("JKS");
            //**这里加载的是根证书链*
            //**密码为生成根证书JKS**时候设置的密码*
            ClassPathResource resource = new ClassPathResource(caJks);
            ks.load(resource.getStream(), caPasswordJks.toCharArray());
            TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509", "SunJSSE");
            tmf.init(ks);
            TrustManager[] tms;
            tms = tmf.getTrustManagers();
            for (int i = 0; i < tms.length; i++) {
                if (tms[i] instanceof X509TrustManager) {
                    sunJSSEX509TrustManager = (X509TrustManager) tms[i];
                    return;
                }
            }
        }

        @Override
        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            try {
                sunJSSEX509TrustManager.checkClientTrusted(chain, authType);
            } catch (CertificateException excep) {
            }
        }

        @Override
        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            try {
                sunJSSEX509TrustManager.checkServerTrusted(chain, authType);
            } catch (CertificateException excep) {
            }
        }

        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return sunJSSEX509TrustManager.getAcceptedIssuers();
        }
    }
}
